package com.hk.zuulservice.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hk.commons.CommonResult;
import com.hk.model.WxPermission;
import com.hk.model.vo.LoginAdmin;
import com.hk.model.vo.Result;
import com.hk.model.vo.ResultVO;
import com.hk.zuulservice.feign.UserFeignClient;
import com.hk.zuulservice.utils.JwtTokenUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: aerfazhe
 * @Date: 2021/8/10 16:31
 * @Statement: 身份权限认证
 */
@Component
public class AuthPermissionFilter extends ZuulFilter {

    @Value("#{'${pathList}'.split(',')}")
    private List<String> pathList;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserFeignClient userFeignClient;

//    判断restFul路径鉴权
    private static AntPathMatcher pathMatcher = new AntPathMatcher();

    private static final Logger logger = LoggerFactory.getLogger(AuthPermissionFilter.class);

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER-1;
    }

    /**
     * 开启过滤
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    /**
     * 1、/admin/tologin 该路径直接放行，不许传递token
     * 2、/admin/loginAdmin 该路径不需要转发直接进行解析Token处理
     * 3、其他路径解析Token,查询权限表所有权限
     * 4、将访问路径和权限表路径进行匹配 有，就查询登陆者所属权限看是否匹配。权限表中没有就直接放行，无需与客户权限进行匹配
     * @return
     * @throws ZuulException
     */
//    Token权限认证
    @Override
    public Object run() throws ZuulException {
//        获取上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        HttpServletResponse response = currentContext.getResponse();
        response.setContentType("application/json;charset=utf-8");
//        获取请求路径
        String requestURI = request.getRequestURI();
//        进行请求路径分割
        String[] split = requestURI.split("/");
        String splitURI = "";
        for (int i = 3; i < split.length; i++) {
            splitURI +="/"+split[i];
        }
//        最终截取的URI
        String finalSplitURI = splitURI;
        boolean boo = pathList.stream().anyMatch(s -> pathMatcher.match(s, finalSplitURI));
        if (boo) { // 登录放行
            return null;
        }
//        不放行解析Token
        String inquireLoginInfo = "/admin/loginAdmin";
        boolean isEquals = StringUtils.equals(splitURI,inquireLoginInfo);
        if (isEquals) { //不放行解析Token
            currentContext.setSendZuulResponse(false);
            //        解析Token获取用户名
            String header = request.getHeader("Authorization");
            String token = header.substring(7);
            boolean tokenExpired = jwtTokenUtil.isTokenExpired(token);
            if (!tokenExpired) { //判断Token是否过期
                //   获取负载
                Claims claims = jwtTokenUtil.getClaimsFromToken(token);
                Integer id = (Integer) claims.get("id");
                Integer loginLogId = (Integer) claims.get("loginLogId");
                String name = (String) claims.get("name");
                LoginAdmin loginAdmin = LoginAdmin.builder().id(id)
                        .name(name).loginLogId(loginLogId).build();
                Result result = CommonResult.successResult(loginAdmin);
                currentContext.setResponseBody(JSONObject.toJSONString(result));
                return null;
            }
            Result result = CommonResult.acResult("暂未登录或token已经过期");
            currentContext.setResponseBody(JSONObject.toJSONString(result));
            return null;
        }
        /**
         * 开始权限校验
         * 1、首先和权限表所有数据进行匹配，未匹配上就直接放行
         * 2、匹配成功，则根据登录Id查询所属权限，再次和登录者所属权限进行匹配，成功则放行，否则不放行，该用户没有此访问权限
         *  todo restful风格默认直接就放行，因为不能精确匹配（需要解决）
         */
//        获取所有权限内容 Feign不能传递对象
        WxPermission permission = WxPermission.builder().type(2).build();
        Integer type = 2;
        ResultVO<WxPermission> permissionResultVO = userFeignClient.pageList(-1, -1, permission,type);
        if (permissionResultVO.getStatus() == 500) {
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseBody(JSON.toJSONString(permissionResultVO));
            return null;
        }
        List<WxPermission> permissionList = permissionResultVO.getList();
        List<String> uriList = permissionList.stream().map(WxPermission::getUri).collect(Collectors.toList());
//        判断在权限表中是否有该路径
        boolean flg = uriList.stream().anyMatch(s -> pathMatcher.match(s, finalSplitURI));
        if (!flg) { //没有直接放行
            return null;
        }
        //        解析Token 获取登录用户ID 判断该用户是否具有权限
        String header = request.getHeader("Authorization");
        String token = header.substring(7);
        boolean tokenExpired = jwtTokenUtil.isTokenExpired(token);
        if (!tokenExpired) { //判断Token是否过期
            //   获取负载
            Claims claims = jwtTokenUtil.getClaimsFromToken(token);
            Integer id = (Integer) claims.get("id");
            Result result = userFeignClient.permissionToCheck(finalSplitURI, id);
            if (result.getStatus() == 200) {
                return null;
            }
            if (result.getStatus() == 403) {
                currentContext.setSendZuulResponse(false);
                currentContext.setResponseBody(JSONObject.toJSONString(result));
                return null;
            }
        }
        return null;
    }



}
